import tkinter as tk
from tkinter import filedialog, ttk, colorchooser, messagebox
import requests
from PIL import Image, ImageTk
import io
import os
import threading
import logging
from pathlib import Path

# 配置客户端日志
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
logger = logging.getLogger(__name__)

class EnhancedIDPhotoClient:
    def __init__(self, root):
        self.root = root
        self.root.title("增强版证件照制作客户端")
        self.root.geometry("1000x1050")
        self.root.minsize(800, 700)
        self.root.resizable(True, True)

        # 基础配置
        self.api_url = tk.StringVar(value="http://192.168.167.238:8000/make_id_photo")
        self.clothes_api_url = tk.StringVar(value="http://192.168.167.238:8000/clothes_options")
        self.image_path = None
        self.processed_image_data = None

        # 初始化界面
        self._init_style()
        self._create_widgets()
        # 加载服装选项
        threading.Thread(target=self.load_clothes_options, daemon=True).start()

    def _init_style(self):
        """初始化控件样式"""
        self.style = ttk.Style()
        self.style.configure("TLabel", font=("微软雅黑", 10))
        self.style.configure("TButton", font=("微软雅黑", 10), padding=5)
        self.style.configure("TCombobox", font=("微软雅黑", 10))
        self.style.configure("TRadiobutton", font=("微软雅黑", 10))
        self.style.configure("TLabelframe.Label", font=("微软雅黑", 11, "bold"))
        # 颜色预览标签样式
        self.style.configure("ColorPreview.TLabel", padding=(5, 8))

    def _create_widgets(self):
        """创建界面控件"""
        # 主框架
        main_frame = ttk.Frame(self.root, padding=10)
        main_frame.pack(fill=tk.BOTH, expand=True)

        # 左侧控制面板（使用Canvas实现滚动）
        control_frame = ttk.LabelFrame(main_frame, text="设置面板", padding=10, width=350)
        control_frame.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 10))
        control_frame.pack_propagate(False)

        # 创建Canvas作为滚动容器
        canvas = tk.Canvas(control_frame)
        
        # 滚动条
        scrollbar = ttk.Scrollbar(control_frame, orient="vertical", command=canvas.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
        # 关联Canvas和滚动条
        canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        canvas.configure(yscrollcommand=scrollbar.set)
        
        # 当Canvas内容变化时更新滚动区域
        canvas.bind("<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all")))

        # 滚动内容容器
        control_content = ttk.Frame(canvas)
        # 将内容容器添加到Canvas
        canvas_window = canvas.create_window((0, 0), window=control_content, anchor="nw")
        
        # 当内容容器大小变化时调整Canvas滚动区域
        control_content.bind("<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all")))
        
        # 当Canvas大小变化时调整内容容器宽度
        def on_canvas_configure(event):
            canvas.itemconfig(canvas_window, width=event.width)
        canvas.bind("<Configure>", on_canvas_configure)

        # 1. API地址设置
        self._add_api_setting(control_content)
        # 2. 图片上传
        self._add_image_upload(control_content)
        # 3. 背景颜色选择（增强反馈）
        self._add_bg_color(control_content)
        # 4. 尺寸设置
        self._add_size_setting(control_content)
        # 5. 美颜设置
        self._add_beauty_setting(control_content)
        # 6. 服装选择（增强错误反馈）
        self._add_clothes_select(control_content)
        # 7. 功能按钮
        self._add_action_buttons(control_content)
        # 8. 状态提示
        self._add_status_label(control_content)

        # 右侧预览区
        preview_frame = ttk.LabelFrame(main_frame, text="预览面板", padding=10)
        preview_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)
        
        # 原始图片预览
        original_frame = ttk.LabelFrame(preview_frame, text="原始图片", padding=5)
        original_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 10))
        self.original_preview = ttk.Label(original_frame, text="请上传图片\n（支持JPG/PNG/BMP）", 
                                         font=("微软雅黑", 12), justify=tk.CENTER)
        self.original_preview.pack(fill=tk.BOTH, expand=True)
        
        # 结果图片预览
        result_frame = ttk.LabelFrame(preview_frame, text="处理结果", padding=5)
        result_frame.pack(fill=tk.BOTH, expand=True)
        self.result_preview = ttk.Label(result_frame, text="等待生成...\n（点击「生成证件照」按钮）", 
                                       font=("微软雅黑", 12), justify=tk.CENTER)
        self.result_preview.pack(fill=tk.BOTH, expand=True)

    # ---------------------- 控件创建方法 ----------------------
    def _add_api_setting(self, parent):
        ttk.Label(parent, text="API地址：").pack(anchor=tk.W, pady=(10, 3))
        api_entry = ttk.Entry(parent, textvariable=self.api_url, width=38)
        api_entry.pack(anchor=tk.W, pady=(0, 10))
        ttk.Label(parent, text="默认：http://192.168.167.238:8000/make_id_photo", 
                 font=("微软雅黑", 8), foreground="#666").pack(anchor=tk.W)

    def _add_image_upload(self, parent):
        ttk.Label(parent, text="原始图片：").pack(anchor=tk.W, pady=(15, 3))
        upload_btn = ttk.Button(parent, text="选择图片", command=self.upload_image)
        upload_btn.pack(anchor=tk.W, pady=(0, 5))
        self.image_name_label = ttk.Label(parent, text="未选择图片", foreground="#888")
        self.image_name_label.pack(anchor=tk.W, pady=(0, 10))

    def _add_bg_color(self, parent):
        ttk.Label(parent, text="背景颜色：").pack(anchor=tk.W, pady=(15, 3))
        self.bg_color_var = tk.StringVar(value="white")
        
        # 颜色选择框（带预览）
        color_frame = ttk.Frame(parent)
        color_frame.pack(anchor=tk.W, pady=(0, 5))
        
        # 白色
        white_frame = ttk.Frame(color_frame)
        white_frame.pack(side=tk.LEFT, padx=8)
        ttk.Radiobutton(white_frame, text="白色", variable=self.bg_color_var, value="white",
                       command=self._on_color_change).pack(anchor=tk.W)
        ttk.Label(white_frame, background="white", width=5, style="ColorPreview.TLabel").pack()
        
        # 红色
        red_frame = ttk.Frame(color_frame)
        red_frame.pack(side=tk.LEFT, padx=8)
        ttk.Radiobutton(red_frame, text="红色", variable=self.bg_color_var, value="red",
                       command=self._on_color_change).pack(anchor=tk.W)
        ttk.Label(red_frame, background="red", width=5, style="ColorPreview.TLabel").pack()
        
        # 蓝色
        blue_frame = ttk.Frame(color_frame)
        blue_frame.pack(side=tk.LEFT, padx=8)
        ttk.Radiobutton(blue_frame, text="蓝色", variable=self.bg_color_var, value="blue",
                       command=self._on_color_change).pack(anchor=tk.W)
        ttk.Label(blue_frame, background="#438EDB", width=5, style="ColorPreview.TLabel").pack()
        
        # 自定义颜色
        custom_frame = ttk.Frame(parent)
        custom_frame.pack(anchor=tk.W, pady=(5, 10))
        self.custom_color_btn = ttk.Button(custom_frame, text="自定义颜色", command=self.choose_custom_color)
        self.custom_color_btn.pack(side=tk.LEFT)
        self.custom_color_preview = ttk.Label(custom_frame, text="    ", background="white", width=8, style="ColorPreview.TLabel")
        self.custom_color_preview.pack(side=tk.LEFT, padx=5)
        
        # 颜色代码显示（帮助调试）
        self.color_code_label = ttk.Label(parent, text="当前颜色代码：white", font=("微软雅黑", 9), foreground="#666")
        self.color_code_label.pack(anchor=tk.W, pady=(0, 5))

    def _add_size_setting(self, parent):
        ttk.Label(parent, text="尺寸设置（像素）：").pack(anchor=tk.W, pady=(15, 3))
        size_frame = ttk.Frame(parent)
        size_frame.pack(anchor=tk.W, pady=(0, 5))
        ttk.Label(size_frame, text="宽度：").pack(side=tk.LEFT, padx=(0, 5))
        self.width_var = tk.IntVar(value=295)
        width_entry = ttk.Entry(size_frame, textvariable=self.width_var, width=10)
        width_entry.pack(side=tk.LEFT, padx=(0, 15))
        ttk.Label(size_frame, text="高度：").pack(side=tk.LEFT, padx=(0, 5))
        self.height_var = tk.IntVar(value=413)
        height_entry = ttk.Entry(size_frame, textvariable=self.height_var, width=10)
        height_entry.pack(side=tk.LEFT)
        
        preset_frame = ttk.Frame(parent)
        preset_frame.pack(anchor=tk.W, pady=(5, 10))
        ttk.Button(preset_frame, text="1寸(295×413)", command=lambda: self.set_size(295, 413)).pack(side=tk.LEFT, padx=5)
        ttk.Button(preset_frame, text="2寸(413×579)", command=lambda: self.set_size(413, 579)).pack(side=tk.LEFT, padx=5)
        ttk.Button(preset_frame, text="小2寸(413×531)", command=lambda: self.set_size(413, 531)).pack(side=tk.LEFT, padx=5)

    def _add_beauty_setting(self, parent):
        ttk.Label(parent, text="美颜强度：").pack(anchor=tk.W, pady=(15, 3))
        self.beauty_level_var = tk.DoubleVar(value=0.3)
        beauty_scale = ttk.Scale(parent, variable=self.beauty_level_var, from_=0, to=1, 
                                orient=tk.HORIZONTAL, length=280, command=self.update_beauty_label)
        beauty_scale.pack(anchor=tk.W, pady=(0, 3))
        self.beauty_label = ttk.Label(parent, text=f"当前强度：{self.beauty_level_var.get():.1f}（0=无美颜，1=最强）")
        self.beauty_label.pack(anchor=tk.W, pady=(0, 10))

    def _add_clothes_select(self, parent):
        ttk.Label(parent, text="服装选择：").pack(anchor=tk.W, pady=(15, 3))
        self.clothes_var = tk.StringVar(value="加载中...")
        self.clothes_combobox = ttk.Combobox(parent, textvariable=self.clothes_var, 
                                            state="readonly", width=35)
        self.clothes_combobox.pack(anchor=tk.W, pady=(0, 5))
        
        # 服装帮助信息（更详细）
        self.clothes_help = ttk.Label(parent, text="提示：请确保API服务已启动，且服装文件夹中有透明PNG图片", 
                                     font=("微软雅黑", 8), foreground="#666")
        self.clothes_help.pack(anchor=tk.W, pady=(0, 5))
        
        # 服装文件夹路径显示
        self.clothes_path_label = ttk.Label(parent, text="服装文件夹：加载中...", 
                                           font=("微软雅黑", 8), foreground="#999", wraplength=300)
        self.clothes_path_label.pack(anchor=tk.W, pady=(0, 10))

    def _add_action_buttons(self, parent):
        btn_frame = ttk.Frame(parent)
        btn_frame.pack(anchor=tk.W, pady=(20, 10))
        self.generate_btn = ttk.Button(btn_frame, text="生成证件照", command=self.start_generate, width=15)
        self.generate_btn.pack(side=tk.LEFT, padx=10)
        self.refresh_clothes_btn = ttk.Button(btn_frame, text="刷新服装", command=self.refresh_clothes, width=15)
        self.refresh_clothes_btn.pack(side=tk.LEFT, padx=10)
        self.save_btn = ttk.Button(btn_frame, text="保存图片", command=self.save_photo, 
                                  width=15, state=tk.DISABLED)
        self.save_btn.pack(side=tk.LEFT, padx=10)

    def _add_status_label(self, parent):
        ttk.Label(parent, text="状态：").pack(anchor=tk.W, pady=(15, 3))
        self.status_var = tk.StringVar(value="就绪：请上传图片并生成")
        self.status_label = ttk.Label(parent, textvariable=self.status_var, foreground="#0066cc")
        self.status_label.pack(anchor=tk.W, pady=(0, 20))

    # ---------------------- 功能逻辑 ----------------------
    def _on_color_change(self):
        """颜色变化时更新预览和状态"""
        color = self.bg_color_var.get()
        self.color_code_label.config(text=f"当前颜色代码：{color}")
        # 更新自定义颜色预览（如果选择了预设颜色）
        if color in ["white", "red", "blue"]:
            color_map = {"white": "white", "red": "red", "blue": "#438EDB"}
            self.custom_color_preview.config(background=color_map[color])

    def update_beauty_label(self, value):
        self.beauty_label.config(text=f"当前强度：{float(value):.1f}（0=无美颜，1=最强）")

    def set_size(self, width, height):
        self.width_var.set(width)
        self.height_var.set(height)

    def upload_image(self):
        file_path = filedialog.askopenfilename(
            title="选择图片",
            filetypes=[("图片文件", "*.jpg *.jpeg *.png *.bmp"), ("所有文件", "*.*")]
        )
        if file_path:
            self.image_path = file_path
            filename = os.path.basename(file_path)
            self.image_name_label.config(text=f"已选择：{filename}"[:30] + "..." if len(filename) > 30 else f"已选择：{filename}")
            self._show_preview(file_path, self.original_preview)

    def _show_preview(self, image_data, label, max_size=(500, 500)):
        try:
            if isinstance(image_data, str):
                img = Image.open(image_data)
            else:
                img = Image.open(io.BytesIO(image_data))
            
            img.thumbnail(max_size, Image.LANCZOS)
            photo = ImageTk.PhotoImage(img)
            label.config(image=photo, text="")
            label.image = photo
        except Exception as e:
            label.config(text=f"预览失败：{str(e)[:20]}", foreground="red")
            self.update_status(f"预览图片错误：{str(e)}", is_error=True)

    def choose_custom_color(self):
        """选择自定义颜色并预览"""
        color = colorchooser.askcolor(title="选择背景色", initialcolor="#ffffff")[1]
        if color:
            self.bg_color_var.set(color)
            self.custom_color_preview.config(background=color)
            self.color_code_label.config(text=f"当前颜色代码：{color}")
            self.update_status(f"已选择自定义颜色：{color}")

    def refresh_clothes(self):
        """手动刷新服装选项"""
        self.update_status("正在刷新服装选项...")
        self.clothes_var.set("刷新中...")
        threading.Thread(target=self.load_clothes_options, daemon=True).start()

    def load_clothes_options(self):
        """加载服装选项（增强错误处理）"""
        try:
            response = requests.get(self.clothes_api_url.get(), timeout=15)
            if response.status_code == 200:
                try:
                    data = response.json()
                    if "error" in data:
                        self.update_status(f"加载服装失败：{data['error']}", is_error=True)
                        self.root.after(0, lambda: self.clothes_path_label.config(text=f"错误：{data['error']}"))
                        return
                    
                    clothes_list = data.get("available_clothes", [])
                    clothes_folder = data.get("clothes_folder", "未知路径")
                    total_count = data.get("total_count", 0)
                    
                    self.root.after(0, lambda: self.clothes_path_label.config(text=f"服装文件夹：{clothes_folder}"))
                    
                    if clothes_list and total_count > 0:
                        self.root.after(0, lambda: self.clothes_combobox.config(values=["不使用服装"] + clothes_list))
                        self.root.after(0, lambda: self.clothes_combobox.current(0))
                        self.update_status(f"加载成功：共{total_count}种服装")
                        self.root.after(0, lambda: self.clothes_help.config(
                            text="提示：选择服装后生成证件照即可应用", 
                            foreground="#009900"
                        ))
                    else:
                        msg = data.get("message", "请将PNG透明图片放入上述文件夹")
                        self.root.after(0, lambda: self.clothes_combobox.config(values=["无可用服装"]))
                        self.root.after(0, lambda: self.clothes_combobox.current(0))
                        self.update_status(f"无可用服装：{msg}", is_warn=True)
                        self.root.after(0, lambda: self.clothes_help.config(
                            text=f"提示：{msg}（必须是透明背景的PNG图片）", 
                            foreground="#ff9900"
                        ))
                except Exception as e:
                    self.update_status(f"解析服装数据失败：{str(e)}", is_error=True)
            else:
                self.update_status(f"加载服装失败：API返回状态码{response.status_code}", is_error=True)
                self.root.after(0, lambda: self.clothes_path_label.config(text=f"API请求失败，状态码：{response.status_code}"))
        except requests.exceptions.ConnectionError:
            error_msg = "无法连接到API，请检查服务是否已启动"
            self.update_status(error_msg, is_error=True)
            self.root.after(0, lambda: self.clothes_combobox.config(values=["API连接失败"]))
            self.root.after(0, lambda: self.clothes_path_label.config(text=error_msg))
        except Exception as e:
            self.update_status(f"加载服装错误：{str(e)}", is_error=True)
            self.root.after(0, lambda: self.clothes_path_label.config(text=f"错误：{str(e)}"))

    def start_generate(self):
        if not self.image_path:
            messagebox.showwarning("缺少图片", "请先上传原始图片！")
            return
        
        self.generate_btn.config(state=tk.DISABLED)
        selected_color = self.bg_color_var.get()
        self.update_status(f"正在生成证件照（颜色：{selected_color}）...")
        threading.Thread(target=self.generate_photo, daemon=True).start()

    def generate_photo(self):
        try:
            files = {"file": open(self.image_path, "rb")}
            clothes_value = self.clothes_var.get()
            clothes_param = clothes_value if clothes_value not in ["无可用服装", "API连接失败", "加载中...", "刷新中..."] else None
            
            bg_color = self.bg_color_var.get()
            logger.info(f"发送颜色参数到API：{bg_color}")
            
            data = {
                "bg_color": bg_color,
                "width": self.width_var.get(),
                "height": self.height_var.get(),
                "beauty_level": self.beauty_level_var.get(),
                "clothes": clothes_param
            }

            response = requests.post(self.api_url.get(), files=files, data=data, timeout=30)
            if response.status_code == 200:
                self.processed_image_data = response.content
                self.root.after(0, lambda: self._show_preview(self.processed_image_data, self.result_preview))
                self.root.after(0, lambda: self.save_btn.config(state=tk.NORMAL))
                self.update_status(f"证件照生成成功（颜色：{bg_color}）")
            else:
                error_msg = f"API返回错误：{response.status_code}"
                logger.error(error_msg)
                self.update_status(error_msg, is_error=True)
                self.root.after(0, lambda: messagebox.showerror("生成失败", f"{error_msg}\n请检查API日志获取详细信息"))
        except Exception as e:
            err_msg = str(e)[:50]
            self.update_status(f"生成错误：{err_msg}", is_error=True)
            self.root.after(0, lambda: messagebox.showerror("生成错误", f"请求失败：{err_msg}\n请检查API地址和网络"))
        finally:
            self.root.after(0, lambda: self.generate_btn.config(state=tk.NORMAL))

    def save_photo(self):
        if not self.processed_image_data:
            messagebox.showwarning("无图片可保存", "请先生成证件照！")
            return
        
        save_path = filedialog.asksaveasfilename(
            title="保存证件照",
            defaultextension=".jpg",
            filetypes=[("JPEG图片", "*.jpg"), ("PNG图片", "*.png"), ("所有文件", "*.*")]
        )
        if save_path:
            try:
                with open(save_path, "wb") as f:
                    f.write(self.processed_image_data)
                self.update_status(f"图片已保存至：{os.path.basename(save_path)}", is_success=True)
                messagebox.showinfo("保存成功", f"图片已保存到：\n{save_path}")
            except Exception as e:
                self.update_status(f"保存失败：{str(e)}", is_error=True)
                messagebox.showerror("保存失败", f"无法保存图片：{str(e)}")

    def update_status(self, msg, is_error=False, is_warn=False, is_success=False):
        color_map = {
            True: "#cc0000",    # 错误：红色
            is_warn: "#ff9900", # 警告：橙色
            is_success: "#009900" # 成功：绿色
        }
        text_color = color_map.get(True, "#0066cc")
        self.root.after(0, lambda: self.status_var.set(msg))
        self.root.after(0, lambda: self.status_label.config(foreground=text_color))


if __name__ == "__main__":
    root = tk.Tk()
    app = EnhancedIDPhotoClient(root)
    root.mainloop()
    